import requests
import random
from random import randint
from requests.packages import urllib3
import time
import asyncio
import zlib
from aiowebsocket.converses import AioWebSocket
import json
urllib3.disable_warnings()
roomid = '21603945'#Minicatty

tim = int(time.time())-10
#打call标志，true时跟随打call
call = False
#重连计数器
tryc = 0
#该函数为发送弹幕时使用
def send(msg):
	print('send:',msg)
	try:
		form_data = {
		'color': '65532',
		'fontsize': '25',
		'mode': '1',
		'msg': msg,
		'rnd': int(time.time()),
		'roomid':roomid,
		'csrf_token':'#######',
		'csrf':'######'
		}
		# 设置cookie值帮助我们在发送弹幕的时候，服务器识别我们的身
		cookie = {
		'cookie':'#######'#在浏览器中发送弹幕，查看post包，含有cookie，csrf,csrf_token,color font
		}
		res = requests.post('https://api.live.bilibili.com/msg/send', headers=cookie, data=form_data)
		if(res.status_code != 200):
			print(res.text)
	except Exception as e:
		print(e)
		

data_raw='000000{headerLen}0010000100000007000000017b22726f6f6d6964223a{roomid}7d'
data_raw=data_raw.format(headerLen=hex(27+len(roomid))[2:], roomid=''.join(map(lambda x:hex(ord(x))[2:],list(roomid))))
msgs = [
'\Mini/\Mini/\Mini/',
'\mini/\mini/\mini/',
'\海豹/\海豹/\海豹/\海豹/\海豹/',
'♡mini♡mini♡mini♡',
'♪mini♪♪mini♪♪mini♪♪',
'⎛⎝≥⏝⏝≤⎛⎝mini⎛⎝≥⏝⏝≤⎛⎝',
'✿mini✿mini✿mini✿'
]


async def startup(url):
	async with AioWebSocket(url) as aws:
		print('task create')
		queue = asyncio.Queue()
		converse = aws.manipulator
		await converse.send(bytes.fromhex(data_raw))
		tasks=[asyncio.create_task(receDM(queue,converse)), asyncio.create_task(sendHeartBeat(converse)),asyncio.create_task(sendMsg(queue)),asyncio.create_task(retry())]
		''',asyncio.create_task(timeout(queue))'''
		#关闭定时打call
		await asyncio.wait(tasks)
		print("task stop")
async def sendMsg(queue):
	print("send th")
	try:
		print("start try")
		while True:
			msg = await queue.get()
			if(msg == 'wait for 1 second'):
				await asyncio.sleep(1)
				continue
			while(len(msg)>20):
				send(msg[:20])
				await asyncio.sleep(1)
				msg = msg[20:]
			send(msg)
			#加入延时防止发送太频繁
			await asyncio.sleep(1)
	except Exception as e:
		print(e)
		print(e.__traceback__.tb_lineno)
		
async def timeout(queue):
	while True:
		#间隔随机秒数发送
		timouts = randint(10,20)
		print('sleep:'+str(timouts))
		await asyncio.sleep(timouts)
		print('wakeup')
		if(call == True):
			await queue.put(msg(randint(len[msg])))
async def retry():
	while True:
		global tryc
		try:
			if(tryc < 2):
				tryc = tryc +1
				await asyncio.sleep(60)
			elif( tryc >= 2):
				tryc = 0
				print('re connect')
				await startup('wss://broadcastlv.chat.bilibili.com:2245/sub')
		except Exception as e:
			print(e)
			print(e.__traceback__.tb_lineno)
		#await queue.put(msg(randint(len[msg])))
#心跳包
hb = '00000010001000010000000200000001'
async def sendHeartBeat(websocket):
	while True:
		await asyncio.sleep(30)
		await websocket.send(bytes.fromhex(hb))
async def receDM(queue,websocket):
	while True:
		#print("recv")
		recv_text = await websocket.receive()
		await printDM(queue,recv_text,0)
# 将数据包传入：
async def printDM(queue,data,deep):
	
	try:
		jd = ''
		deep += 1
		packsize = int(data[1:4].hex(),16)
		headlen = int(data[5:6].hex(),16)
		ver = int(data[7:8].hex(),16)
		op = int(data[9:12].hex(),16)
		sequ = int(data[13:16].hex(),16)
		
	# 有的时候可能会两个数据包连在一起发过来，所以利用前面的数据包长度判断，
		if(len(data)>packsize):
			await printDM(queue,data[packsize:],deep)
		data=data[16:packsize]
	# 有时会发送过来 zlib 压缩的数据包，这个时候要去解压.
	#try:
		if(ver == 2):
			data = zlib.decompress(data)
			await printDM(queue,data,deep)
			return
	    # ver 为1的时候为进入房间后或心跳包服务器的回应。op 为3的时候为房间的人气值。
		elif(ver == 1):
			global tryc
			if(len(data)>4):
				#超时重试连接
				#心跳服务器回应
				global tryc
				tryc = 0
			elif(op == 3):
				print('直播间热度:  {}'.format(int(data.hex(),16)))
			return
#print(e)
    # ver是0是普通的 json 数据。
    # op 为5意味着这是通知消息。
		elif(op==5):
			jd = json.loads(data.decode('utf-8', errors='ignore'))
			if(jd['cmd']=='DANMU_MSG'):
				#print(jd)
				#print(jd['info'][3][0],jd['info'][2][1],jd['info'][1])
				medal = ''
				#获取粉丝牌
				try:
					medal = jd['info'][3][1]
				except Exception as e:
					medal = ''
				print("弹幕",medal,jd['info'][2][1], ': ', jd['info'][1])
				uname = jd['info'][2][1]
				msg = jd['info'][1]
				global call
				if(uname == '惑桜'):
					if(msg == ('\Mini/\Mini/') and call == False):
						print("开始打CALL")
						call = True
					elif(msg == ('\Mimi/') and call == True):
						call = False
						print("停止CALL")
				if(msg.find('早上好') != -1 or msg.find('早安') != -1 or msg.find('mini早') != -1 or msg.find('Mini早') != -1):
					print(uname)
					if(uname not in ulist):
						ulist.append(uname)
						#await queue.put(uname + " 早上好~")
						print(ulist)
					else:
						#await queue.put(uname + " 不准毛早上好")
						print(ulist)
				if(call == True):
					if(msg.count('mini') >= 3 or msg.count('Mini') >=3 or msg.count('馒头') >=3 or msg.count('海豹') >=3 or msg.find('⎛⎝≥⏝⏝≤⎛⎝mini⎛⎝≥⏝⏝≤⎛⎝') != -1):
						global tim
						#防止发送太快
						if(int(time.time())-tim > randint(5,8)):
							tim = int(time.time())
							await queue.put(msgs[randint(0,6)])
			elif(jd['cmd']=='SEND_GIFT'):
				print('send gift')
				try:
					gift = jd['data']['giftName']
				except Exception as e:
					gift = jd['data']['gift_name']
				if(jd['data']['num'] == 1):
					print('感谢 '+jd['data']['uname']+ ' '+ jd['data']['action']+ ' '+ gift)
				else:
					print('感谢 '+jd['data']['uname']+ ' '+ jd['data']['action']+ ' '+ str(jd['data']['num'])+ '个'+ gift)
				if(gift == '典藏星令'):
					await queue.put('感谢'+jd['data']['uname']+'的典藏星令')
				elif(gift == '普通星令'):
					await queue.put('感谢'+jd['data']['uname']+'的星令')
			elif(jd['cmd']=='LIVE'):
				print('[Notice] LIVE Start!')
				await queue.put('早上好')
			elif(jd['cmd']=='PREPARING'):
				print('[Notice] LIVE Ended!')
			elif(jd['cmd']=='INTERACT_WORD'):
				#print(jd)
				if(jd['data']['msg_type'] == 2):
				#关注
					await queue.put('感谢 '+jd['data']['uname'] +' 的关注')
					
				else:
					print('')
				#jd['data']['uname']
				#进入直播间
			elif(jd['cmd'] == 'COMBO_SEND'):
				gift = ''
				try:
					gift = jd['data']['giftName']
				except Exception as e:
					gift = jd['data']['gift_name']
				print('感谢'+jd['data']['uname']+jd['data']['action'] + str(jd['data']['combo_num'])+" 个 "+gift)
			elif(jd['cmd']=='STOP_LIVE_ROOM_LIST'):
				print('STOP_LIVE_ROOM_LIST')	
			elif(jd['cmd']=='NOTICE_MSG'):
				print('NOTICE_MSG')
			elif(jd['cmd'] == 'SUPER_CHAT_MESSAGE' or jd['cmd'] == 'SUPER_CHAT_MESSAGE_JPN'):
			#super chat
				print('感谢 ',jd['data']['user_info']['uname'],' 的 SUPERCHAT ',jd['data']['message'])
			elif(jd['cmd'] == 'ENTRY_EFFECT'):
				print(jd['data']['copy_writing'])
			elif(jd['cmd'] == 'ONLINE_RANK_V2'):
				print('ONLINE_RANK_V2')
			elif(jd['cmd'] == 'WIDGET_BANNER'):
				print('WIDGET_BANNER')
			elif(jd['cmd'] == 'HOT_RANK_CHANGED'):
			#高能榜
				print('HOT_RANK_CHANGED')
			elif(jd['cmd'] == 'HOT_RANK_SETTLEMENT'):
			#高能榜
				print('HOT_RANK_SETTLEMENT')
			elif(jd['cmd'] == 'GUARD_BUY'):
				print('感谢 '+jd['data']['user_name']+' 的 '+jd['data']['gift_name'])
				print('老板大气')
			elif(jd['cmd'] == 'USER_TOAST_MSG'):
				print('感谢 ',jd['data']['toast_msg'])
				print('老板大气')
			else:
				print('[OTHER] ', jd['cmd'])
				print(jd)
			return
		print('unknown')
		print('ver:'+str(ver)+"\top:"+str(op))
		print(json.loads(data.decode('utf-8', errors='ignore')))
	except Exception as e:
		print(e)
		print(e.__traceback__.tb_lineno)
		print(jd)
if __name__ == '__main__':
	remote = 'wss://broadcastlv.chat.bilibili.com:2245/sub'
	task1 = asyncio.run(startup(remote))
